# 09 Class
pass
'''类提供了一种组合数据和功能的方法。
Python 的类提供了面向对象编程的所有标准特性：
类继承机制允许多个基类，
派生类可以覆盖它基类的任何方法，
一个方法可以调用基类中相同名称的的方法。

对象可以包含任意数量和类型的数据。

和模块一样，类也拥有 Python 天然的动态特性：
它们在运行时创建，可以在创建后修改。
'''

## 9.1. 名称和对象

'''对象具有个性，多个名称（在多个作用域内）可以绑定到同一个对象
别名
'''

## 9.2 Python 作用域和命名空间

'''(刚开始有点绕, 需要慢慢理解)
'''

'''namespace （命名空间）是一个从名字到对象的映射
任何跟在一个点号之后的名称都称为 属性 
按严格的说法，对模块中名称的引用属于属性引用

属性可以是只读或者可写的

模块属性是可以写，你可以写出 modname.the_answer = 42 。
可写的属性同样可以用 del 语句删除。
例如， del modname.the_answer 将会从名为 modname 的对象中移除 the_answer 属性。

在不同时刻创建的命名空间拥有不同的生存期。

一个 作用域 是一个命名空间可直接访问的 Python 程序的文本区域

nonlocal : 声明为非本地变量

如果一个名称被声明为全局变量，则所有引用和赋值将直接指向包含该模块的全局名称的中间作用域。 要重新绑定在最内层作用域以外找到的变量，可以使用 nonlocal 语句声明为非本地变量。 如果没有被声明为非本地变量，这些变量将是只读的（尝试写入这样的变量只会在最内层作用域中创建一个 新的 局部变量，而同名的外部变量保持不变）。

global 语句
可被用来表明特定变量生存于全局作用域并且应当在其中被重新绑定；

nonlocal 语句
表明特定变量生存于外层作用域中并且应当在其中被重新绑定。
'''

'''
示例见 scopes_and_namespaces.py
'''

## 9.3. 初探类
'''
三种新对象类型
一些新语义
'''

### 9.3.1. 类定义语法
'''类定义与函数定义 (def 语句) 一样必须被执行才会起作用
'''

### 9.3.2 类对象

'''两种操作
属性引用  `obj.name`
实例化
'''

class MyClass:
    """ A simple example class"""
    name = 'none'

    def __init__(self, name):
        self.name=name

    def print(self, str):
        print(str)

print(MyClass.__name__, MyClass.__doc__)

'''属性引用
'''
print(MyClass.name) # 'none'

''' 实例化
'''
x = MyClass(name='ming')
print(x.name)

print("延超","得分","dfd", sep="xxxx")

### 9.3.3. 实例对象
'''属性引用
- 数据属性
- 方法
'''

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter) # 16


### 9.3.4. 方法对象
'''通常在绑定后立即调用
'''
x.print("方法对象")

### 9.3.5. 类和实例变量
'''实例变量用于每个实例的唯一数据
而类变量用于类的所有实例共享的属性和方法:
'''
class Dog:

    kind = 'canine' # class variable shared by all instances 所有实例共享类变量

    def __init__(self, name):
        self.name = name  #  instance variable unique to each instance 每个实例唯一的实例变量

'''正确的类设计应该使用实例变量
'''

class Dog2:
    def __init__(self, name):
        self.name = name
        self.tricks = []

    def add_trick(self, trick):
        self.tricks.append(trick)

d2 = Dog2("Fido");
d2.add_trick('roll over')
d22 = Dog2('Buddy')
d22.add_trick('play dead')
d22.add_trick('play dead 2')
print(d2.tricks)            # ['roll over']
print(d22.tricks)           # ['play dead', 'play dead 2']

## 9.4. 补充说明

# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1

    def g(self):
        return 'hello function'

    h = g


print(C.__class__)          # <class 'type'>

## 9.5. 继承

'''派生类定义语法: '''
class A:
    name = 'base'

class Aa(A):
    age = 12

'''名称 BaseClassName 必须定义于包含派生类定义的作用域中

class DerivedClassName(modname.BaseClassName):
'''

'''两个内置函数
isinstance(object, classinfo)   检查示例类型

issubclass(object, classinfo)   检查继承关系
'''

#### 9.5.1 多重继承
''' 多个基类定义语法
class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

搜索从父类所继承属性的操作是深度优先、从左至右的，当层次结构中存在重叠时不会在同一个类中搜索两次。
方法解析顺序会动态改变以支持对 super() 的协同调用。
这种方式在某些其他多重继承型语言中被称为后续方法调用，它比单继承型语言中的 super 调用更强大。
动态改变顺序是有必要的，因为所有多重继承的情况都会显示出一个或更多的菱形关联（即至少有一个父类可通过多条路径被最底层类所访问）
'''

print('--------------------------------------------------- 私有变量 ------------------------------------------------')
## 9.6. 私有变量
'''那种仅限从一个对象内部访问的“私有”实例变量在 Python 中并不存在。
但是，大多数 Python 代码都遵循这样一个约定：
带有一个下划线的名称 (例如 _spam) 应该被当作是 API 的非公有部分 (无论它是函数、方法或是数据成员)。 
这应当被视为一个实现细节，可能不经通知即加以改变。
'''
'''例如: 避免名称与子类所定义的名称相冲突
'''
class Mapping:
    _desc = '基类私有变量'

    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        print("基类: " + self._desc)
        for item in iterable:
            self.items_list.append(item)

    # 转换为 _Mapping__update
    __update = update       # private copy of original update() method


class MappingSubclass(Mapping):
    # 继承后, 改写名称为 _MappingSubclass__update

    # provides new signature for update()
    # but does note break __init__()
    def update(self, keys, values):
        print("子类: " + super()._desc)
        for item in zip(keys, values):
            self.items_list.append(item)


m = Mapping(['a', 'b', 'c'])
print(m.items_list)                 # ['a', 'b', 'c']

'''
这时候, 创建子类调用的是父类的初始化方法, 所以会打印 '基类: 基类私有变量'
'''
m_sub = MappingSubclass(['x', 'y', 'z'])
'''
这里执行的是子类的 update() 所以是打印 '子类: 基类私有变量' 
'''
m_sub.update('abc', 'xyz')
print(m_sub.items_list)             # ['x', 'y', 'z', ('a', 'x'), ('b', 'y'), ('c', 'z')]

## 9.7 杂项说明

class Employee:
    pass

john = Employee()       # Create an empty employee record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

## 9.8. 迭代器

for element in [1, 2, 3]:
    print(element, end='-')

for element in (4, 8, 9):
    print(element, end='-')

for key in {'one': 1, 'two' : 2}:
    print(key, end=' , ')

for char in "123":
    print(char)

'''
for line in open('file.txt'):
'''

class Reverse:
    """Iterator for looping over a sequence backwards"""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index -1
        return self.data[self.index]


rev = Reverse('span')
iter(rev)
for char in rev:
    print(char, end=' ')

print()

## 9.9. 生成器
print('--------------------------------------------------- 生成器 ------------------------------------------------')

# 生成器自动生成 __iter__() 和 __next__() 方法

def reverse(data):
    for index in range(len(data) -1, -1, -1):
        yield data[index]               # 执行next() 时候, 执行语句

for char in reverse('golf'):
    print(char, end= ' ')

## 9.10. 生成器表达式
print()
x = sum(i*i for i in range(5))
print(x)
data = 'golf'
d = list(data[i] for i in range(len(data) -1, -1, -1))
print(d)  # ['f', 'l', 'o', 'g']

